-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local json = require 'cjson'
local bs_util = require 'util.base_util'
local prop_def = require "macros.property_def"
local class = require 'mc.class'
local log = require 'mc.logging'
local org_freedesktop_dbus = require 'sd_bus.org_freedesktop_dbus'
local initiator = require 'mc.initiator'
local skynet_queue = require 'skynet.queue'
local obj_def = require 'macros.object_def'
local msg = require 'bios.ipmi.ipmi_message'
local bios_factory = require 'factory.bios_factory'
local ipmi = require 'ipmi'
local skynet = require 'skynet'
local s_pack = string.pack
local s_unpack = string.unpack
local vos = require 'utils.vos'
local base_messages = require 'messages.base'
local comp_code = ipmi.types.Cc
local match_rule = org_freedesktop_dbus.MatchRule
local utils = require 'mc.utils'
local syslog_core = require 'syslog.core'
local file_util = require 'infrastructure.file_util'
local utils_core = require 'utils.core'
local firmware_collection = require 'interface.mdb.firmware_multihost_collection'
local component_collection = require 'domain.mapping.component_manager_collection'
local menu_config = require 'domain.config.Config'
local bios_object = class()
local event = require 'infrastructure.event'
local client = require 'bios.client'

local function get_valid_version(version)
    local valid_version = ''
    for name in string.gmatch(version, '[%w.]') do
        valid_version = valid_version .. name
    end
    return valid_version
end

local function get_private_obj()
    return {
        bios_id = 0x0,
        manu_id = 0xDB0700,
        OldSetupPassword = '',
        NewSetupPassword = '',
        UserOldSetupPassword = '',
        UserNewSetupPassword = '',
        BiosJsonFlag = 1,
        SecureBootNewFile = '',
        SecureBootCurrentFile = '',
        CMESFileName = '',
        PsuInfoFileName = '',
        FileChangeFlag = 0,
        SilkPath = '',
        SilkBakPath = ''
    }
end

local JSON_FILES<const> = {
    CurrentValueFileName = 'currentvalue.json',
    ResultFileName = 'result.json',
    SettingFileName = 'setting.json',
    RegistryFileName = 'registry.json'
}

local SILK_FILES<const> = {
    SilkName = 'silkconfig.json',
    SilkBakName = 'silkconfig_bak.json'
}

function bios_object:ctor(obj, db, bus, position)
    self.db = db
    self.obj = obj
    self.bus = bus
    self.position = position
    self.system_id = obj:get_system_id()
    self.private_obj = get_private_obj()
    self.memory_info_path = string.format('/bmc/kepler/Systems/%s/Memory', self.system_id)
    self.firmware = firmware_collection.get_firmware(self.system_id, bus, db)
    self.cached_mem_info = {}
    self.queue = skynet_queue()
    self.task_status = {}
    self.task_status[0] = prop_def.BIOS_CHANGE_PWD_TASK_IDLE
    self.task_status[1] = prop_def.BIOS_CHANGE_PWD_TASK_IDLE
    self.cpt_manager = component_collection.get_component(self.system_id)
    self.cached_mem_info = {}
    self.mem_info_by_path = {}
    self.mem_silk_array = {}
    self.pcie_list = {}
    self.sas_list = {}
end

function bios_object:init()
    self:select_info_db()
    self:select_config_db()
    self:init_prop()
    self:async_fetch_info()
end

function bios_object:async_fetch_info()
    pcall(function()
        log:notice('[bios]start async fetch component version info, system is %s', self.system_id)
        self.cpt_manager:async_fetch_info()
    end)
end

function bios_object:fetch_info()
    pcall(function()
        log:notice('[bios]start fetch component version info, system is %s', self.system_id)
        self.cpt_manager:fetch_info()
    end)
end

function bios_object:cache_info(system_id)
    local object_service = bios_factory.get_service('object_service')
    if not object_service then
        return {}
    end
    local path = string.format('/bmc/kepler/Systems/%s/PCIeDevices/PcieAddrInfo', system_id)
    return object_service:get_object_list(obj_def.PCIE_DEVICE_SERVICE_NAME,
        path, obj_def.PCIE_ADDR_INFO_INTERFACE)
end
 
function bios_object:cache_pcie_info()
    self.pcie_list = self:cache_info(self.system_id)
    if self.system_id == prop_def.DEFAULT_SYSTEM_ID then
        self.sas_list = self.pcie_list
    else
        self.sas_list = self:cache_info(prop_def.DEFAULT_SYSTEM_ID)
    end
end

function bios_object:cache_cpu_info()
    self.cpu_list = {}
    local node_obj = {}
    client:ForeachCPUObjects(function(obj)
        if obj.LogicalId and obj.PhysicalId then
            node_obj = {}
            node_obj[prop_def.BIOS_SIKL_JSON_PHYSICAL_ID] = obj.PhysicalId
            node_obj[prop_def.BIOS_SIKL_JSON_LOGICAL_ID] = obj.LogicalId
            node_obj[prop_def.BIOS_SILK_JSON_SILK] = 'CPU' .. tostring(obj.PhysicalId)
            node_obj[prop_def.CPU_DEVICE_LOCATOR] = obj.DeviceLocator
            table.insert(self.cpu_list, node_obj)
        end
    end)
    return self.cpu_list
end
 
function bios_object:get_sas_info()
    if not self.sas_list or not next(self.sas_list) then
        log:notice('[bios]need cache sas info')
        self.sas_list = self:cache_info(prop_def.DEFAULT_SYSTEM_ID)
    end
    return self.sas_list
end
 
function bios_object:get_pcie_info()
    if not self.pcie_list or not next(self.pcie_list) then
        log:notice('[bios]need cache pcie info')
        self.pcie_list = self:cache_info(self.system_id)
    end
    return self.pcie_list
end

function bios_object:get_cpu_info()
    if not self.cpu_list or not next(self.cpu_list) then
        log:notice('[bios]need cache cpu info')
        self.cpu_list = self:cache_cpu_info(self.system_id)
    end
    return self.cpu_list
end
 
function bios_object:start_cache_pcie(is_multihost)
    skynet.fork(function()
        local times = 0
        log:notice('[bios]start cache system %s pcie info', self.system_id)
        if not is_multihost then
            self:cache_cpu_info()
        end
        while times < 3 do
            self:cache_pcie_info()
            times = times + 1
            skynet.sleep(10 * 100)
        end
        log:notice('[bios]finish cache system %s pcie info', self.system_id)
    end)
end

function bios_object:init_path()
    if self.system_id ~= prop_def.DEFAULT_SYSTEM_ID then
        utils.mkdir_with_parents(prop_def.BIOS_CONFIG_PATH .. '/' .. self.system_id,
            utils.S_IRWXU | utils.S_IRGRP | utils.S_IXGRP)
    end
    bs_util.new_file(self.obj.CurrentValueFileName)
    bs_util.new_file(self.obj.ResultFileName)
    bs_util.new_file(self.obj.SettingFileName)
    bs_util.new_file(self.obj.RegistryFileName)
    utils_core.chmod_s(self.obj.CurrentValueFileName, utils.S_IRUSR | utils.S_IWUSR)
    utils_core.chmod_s(self.obj.ResultFileName, utils.S_IRUSR | utils.S_IWUSR)
    utils_core.chmod_s(self.obj.SettingFileName, utils.S_IRUSR | utils.S_IWUSR)
    utils_core.chmod_s(self.obj.RegistryFileName, utils.S_IRUSR | utils.S_IWUSR)
end

function bios_object:init_prop()
    local obj = self.obj
    local info_db = self.info_db
    local config_db = self.config_db
    local private_obj = self.private_obj
    obj.MenuData = ''
    obj.MenuChangeFlag = 0
    obj.DEMTConfig = 0

    obj.Version = get_valid_version( info_db.Version)
    obj.BackupVersion = get_valid_version(info_db.BackupVersion)
    obj.RegistryVersion = info_db.RegistryVersion
    obj.RecoverFailed = info_db.RecoverFailed
    obj.UpgradeFailed = info_db.UpgradeFailed
    obj.TeeOSVersion = info_db.TeeOSVersion
    obj.SystemStartupState = info_db.SystemStartupState
    obj.ResetBiosToDefaultsPending = info_db.ResetBiosToDefaultsPending
    obj.BiosBootStage = info_db.BiosBootStage

    obj.CurrentValueFileName = bs_util.get_data_path(self.system_id, 'currentvalue.json')
    obj.ResultFileName = bs_util.get_data_path(self.system_id, 'result.json')
    obj.SettingFileName = bs_util.get_data_path(self.system_id, 'setting.json')
    obj.RegistryFileName = bs_util.get_data_path(self.system_id, 'registry.json')

    private_obj.bios_id = obj.SystemId
    private_obj.manu_id = 0xDB0700
    private_obj.BiosJsonFlag = 1
    private_obj.FileChangeFlag = config_db.FileChangeFlag
    private_obj.FileChannel = config_db.FileChannel
    private_obj.FileNum = config_db.FileNum
    private_obj.OldSetupPassword = info_db.OldSetupPassword
    private_obj.NewSetupPassword = info_db.NewSetupPassword
    private_obj.UserOldSetupPassword = info_db.UserOldSetupPassword
    private_obj.UserNewSetupPassword = info_db.UserNewSetupPassword
    private_obj.SecureBootNewFile = bs_util.get_conf_path(self.system_id, prop_def.NEW_SECUREBOOT_FILE)
    private_obj.SecureBootCurrentFile = bs_util.get_conf_path(self.system_id, prop_def.CURRENT_SECUREBOOT_FILE)
    private_obj.CMESFileName = bs_util.get_conf_path(self.system_id, prop_def.CMES_DAT)
    private_obj.PsuInfoFileName = bs_util.get_conf_path(self.system_id, prop_def.PSU_INFO)
    private_obj.SilkPath = bs_util.get_conf_path(self.system_id, prop_def.SILK_NAME)
    private_obj.SilkBakPath = bs_util.get_conf_path(self.system_id, prop_def.SILK_BAK_NAME)

    skynet.fork(function()
        self:init_path()
        self:init_effective_status()
        self:update_registry_version()
        self:update_print_flag()
        self:build_registry_config()
    end)
end

-- 向前兼容，当system_id为1时，先获取BootInfo_1的数据，如果没有则使用BootInfo的数据创建新数据
function bios_object:select_info_db()
    local db_id = 'BiosInfo'
    if self.system_id ~= prop_def.DEFAULT_SYSTEM_ID then
        db_id = 'BiosInfo_' .. self.system_id
    end
    local db_info = self.db:select(self.db.BiosInfoTable)
        :where(self.db.BiosInfoTable.Id:eq(db_id)):first()
    if db_info then
        self.info_db = db_info
        return
    end
    local new_db_info = self.db.BiosInfoTable({Id = db_id,
        Version = '000',
        BackupVersion = '000',
        OldSetupPassword = '',
        NewSetupPassword = '',
        UserOldSetupPassword = '',
        UserNewSetupPassword = '',
        SystemStartupState = 0,
        ResetBiosToDefaultsPending = false,
        BiosBootStage = 0})
    new_db_info:save()
    self.info_db = new_db_info
end

-- 向前兼容，当system_id为1时，先获取BiosCfgInfo_1的数据，如果没有则使用BiosCfgInfo的数据创建新数据
function bios_object:select_config_db()
    local db_id = 'BiosCfgInfo'
    if self.system_id ~= prop_def.DEFAULT_SYSTEM_ID then
        db_id = 'BiosCfgInfo' .. self.system_id
    end
    local db_info = self.db:select(self.db.BiosConfigTable)
        :where(self.db.BiosConfigTable.Id:eq(db_id)):first()
    if db_info then
        self.config_db = db_info
        return
    end
    local new_db_info = self.db.BiosConfigTable({Id = db_id,
        FileChangeFlag = 0,
        FileChannel = 0,
        FileNum = 0})
    new_db_info:save()
    self.config_db = new_db_info
end


function bios_object:init_effective_status()
    local status = 'Effective'
    if self.private_obj.FileChangeFlag == prop_def.BIOS_SETTING_FILE_CHANGED then
        status = 'Ineffective'
    end
    self:set_prop('SettingsEffectiveStatus', status)
    log:notice('[bios](systemId:%s)init setting effective status to %s', self.obj.SystemId, status)
end

function bios_object:update_registry_version()
    local registry_json = bs_util.get_file_json(self.obj.RegistryFileName)
    if not registry_json or not registry_json['RegistryVersion'] then
        log:info('bios_registry_version_update: (systemId:%s) get registry json empty', self.obj.SystemId)
        self:set_prop('RegistryVersion', 'V0.00')
        return
    end

    self:set_prop('RegistryVersion', registry_json['RegistryVersion'])
    log:notice('bios_registry_version_update:(systemId:%s)Bios RegistryVersion update to %s',
        self.obj.SystemId, registry_json['RegistryVersion'])
end

-- 导入配置：更新待生效值、更新依赖关系
function bios_object:update_denpendency()
    if not self.config or self:get_prop('SettingsEffectiveStatus') == 'Effective' then
        return
    end
    self.config:update_setting_value_by_file()
    self.config:update_denpendency_after_clear()
    log:notice('[bios]system %s update denpendency success', self.system_id)
end

-- 上报currentvalue：清除setting、更新current、更新依赖关系
function bios_object:deal_current()
    bs_util.clear_file(self.obj.SettingFileName)
    if not self.config then
        return
    end
    self.config:clear_setting_value()
    self.config:update_current_value_by_file()
    self.config:update_denpendency_after_clear()
    log:notice('[bios]system %s update config current value', self.system_id)
end

-- 清除配置：清除setting值、更新依赖关系
function bios_object:clear_setting()
    if not self.config then
        return
    end
    self.config:clear_setting_value()
    self.config:update_denpendency_after_clear()
    log:notice('[bios]system %s clear setting value', self.system_id)
end

-- 生成config时机：BMC重启、带内上报Registery文件
function bios_object:new_config()
    self.config = menu_config.new(self.obj.RegistryFileName,
        self.obj.SettingFileName, self.obj.CurrentValueFileName)
    log:notice('[bios]system %s new web config', self.system_id)
end

function bios_object:deal_registry()
    self:update_registry_version()
    self:new_config()
end

-- 更新currentvalue时机：BMC重启、带内上报currentvalue
function bios_object:init_current_value()
    if not self.config then
        return
    end
    self.config:update_current_value_by_file() 
    log:notice('[bios]system %s update web config current value', self.system_id)
end

-- 更新setting时机：BMC重启根据生效状态、带内重启需要将生效值清空、导入配置
function bios_object:init_config_value()
    if not self.config or self:get_prop('SettingsEffectiveStatus') == 'Effective' then
        return
    end
    self.config:update_setting_value_by_file()
end

-- BMC重启，生成配置
function bios_object:build_registry_config()
    local ok, res = pcall(function()
        self:new_config()
        self:init_current_value()
        self:init_config_value()
    end)
    if ok then
        log:notice('[bios]build system %s registry config', self.system_id)
    else
        log:error('[bios]build system %s registry config fail, err %s', self.system_id, res)
    end
end

local START_FINISH<const> = 254
function bios_object:check_config(selector)
    if selector ~= prop_def.BIOS_FILE_REGISTRY_NUM and
        selector ~= prop_def.BIOS_FILE_CURRENT_VALUE_NUM then
        return true
    end

    local state = self:get_prop('SystemStartupState')
    -- 启动完成不能重新上报currentvalue、registery文件
    if state == START_FINISH then
        log:error('[bios]system(%s) startup state(%s) selector(%s) inavlid',
            self.system_id, state, selector)
        return false
    end
    return true
end

function bios_object:get_current_value_setting(key)
    local data = bs_util.safe_get_file_json(self.obj.CurrentValueFileName)
    if not data then
        error(base_messages.InternalError())
    end
    local value = data[key]
    if value == nil then
        error(base_messages.InternalError())
    end
    return tostring(value)
end

function bios_object:update_print_flag()
    local data = bs_util.safe_get_file_json(self.obj.CurrentValueFileName)
    if data and data['SYSDBGLevel'] then
        self:set_print_flag(data['SYSDBGLevel'])
    end
end

function bios_object:extract_add_memsilk(object)
    local node_obj = {}
    node_obj[prop_def.BIOS_SILK_JSON_SOCKET_ID] = object.CpuId
    node_obj[prop_def.BIOS_SILK_JSON_PHY_CHANNEL_ID] = object.ChannelId
    node_obj[prop_def.BIOS_SILK_JSON_LOGICAL_CHANNEL_ID] = object.LogicalChannelId
    node_obj[prop_def.BIOS_SILK_JSON_DIMM_ID] = object.DimmId
    node_obj[prop_def.BIOS_SILK_JSON_SILK] = object.DimmName
    table.insert(self.mem_silk_array, node_obj)
end

function bios_object:add_memsilk(object)
    skynet.fork(function()
        self:extract_add_memsilk(object)
    end)
end

function bios_object:get_memory_silk_config()
    local mem_obj_array = {}
    local obj_util = bios_factory.get_service('object_service')
    if not obj_util then
        return
    end

    local mem_info_list = obj_util:get_object_list(obj_def.COMPUTE_SERVICE_NAME,
        self.memory_info_path, obj_def.MEMORY_INFO_INTERFACE)

    log:info('get_memory_silk_config: MemoryAddrInfo count=%s, system id: %s', #mem_info_list, self.system_id)
    for _, obj in pairs(mem_info_list) do
        if obj and not self.mem_info_by_path[obj.path] then
            local node_obj = {}
            node_obj[prop_def.BIOS_SILK_JSON_SOCKET_ID] = obj[obj_def.PROPERTY_MEM_CPU_ID]:value()
            node_obj[prop_def.BIOS_SILK_JSON_PHY_CHANNEL_ID] =
                obj[obj_def.PROPERTY_MEM_CHANNEL_ID]:value()
            node_obj[prop_def.BIOS_SILK_JSON_LOGICAL_CHANNEL_ID] =
                obj[obj_def.PROPERTY_LOGICAL_CHANNEL_ID]:value()
            node_obj[prop_def.BIOS_SILK_JSON_DIMM_ID] = obj[obj_def.PROPERTY_MEM_DIMM_ID]:value()
            node_obj[prop_def.BIOS_SILK_JSON_SILK] = obj[obj_def.PROPERTY_MEM_NAME]:value()
            table.insert(self.cached_mem_info, node_obj)
            self.mem_info_by_path[obj.path] = node_obj  -- 防止重复获取
        end
    end
    return mem_obj_array
end

function bios_object:handle_memory_silk_config_add(path, props)
    -- 防止重复获取
    if self.mem_info_by_path[path] then
        return
    end
 
    self.mem_info_by_path[path] = {
        [prop_def.BIOS_SILK_JSON_SOCKET_ID] = props[obj_def.PROPERTY_MEM_CPU_ID]:value(),
        [prop_def.BIOS_SILK_JSON_PHY_CHANNEL_ID] = props[obj_def.PROPERTY_MEM_CHANNEL_ID]:value(),
        [prop_def.BIOS_SILK_JSON_LOGICAL_CHANNEL_ID] = props[obj_def.PROPERTY_LOGICAL_CHANNEL_ID]:value(),
        [prop_def.BIOS_SILK_JSON_DIMM_ID] = props[obj_def.PROPERTY_MEM_DIMM_ID]:value(),
        [prop_def.BIOS_SILK_JSON_SILK] = props[obj_def.PROPERTY_MEM_NAME]:value()
    }
 
    table.insert(self.cached_mem_info, self.mem_info_by_path[path])
end
 
function bios_object:handle_memory_silk_config_del(path)
    self.mem_info_by_path[path] = nil
    -- 重新生成cached_mem_info
    self.cached_mem_info = {}
    for _, v in pairs(self.mem_info_by_path) do
        table.insert(self.cached_mem_info, v)
    end
end

function bios_object:cache_mem_silk_info()
    -- 监听资源树变化
    local add = org_freedesktop_dbus.ObjMgrInterfacesAdded
    local add_sig = match_rule.signal(add.name):with_interface(add.interface):with_path_namespace(
        self.memory_info_path)

    -- 如果有增加或者删减，重新获取内存丝印信息
    self.add_slot = self.bus:match(add_sig, function(msg)
        local path, interfaces_and_properties = msg:read('oa{sa{sv}}')
        local props = interfaces_and_properties[obj_def.MEMORY_INFO_INTERFACE]
        if not props then
            return
        end
        self.queue(function()
            self:handle_memory_silk_config_add(path, props)
        end)
    end)

    local del = org_freedesktop_dbus.ObjMgrInterfacesRemoved
    local del_sig = match_rule.signal(del.name):with_interface(del.interface):with_path_namespace(
        self.memory_info_path)
    self.del_slot = self.bus:match(del_sig, function(msg)
        local path, interfaces = msg:read('oas')
        if not interfaces[obj_def.MEMORY_INFO_INTERFACE] then
            return
        end
        self.queue(function()
            self:handle_memory_silk_config_del(path)
        end)
    end)
    -- 提前获取一下
    self.queue(function()
        self:get_memory_silk_config()
    end)
end

function bios_object:get_mem_silk_array()
    pcall(function()
        log:notice('get_mem_silk_array: MemoryAddrInfo count=%s, system id: %s', #self.mem_silk_array, self.system_id)
    end)
    return self.mem_silk_array
end

function bios_object:get_cahche_mem_info()
    pcall(function()
        log:notice('get_cahche_mem_info: MemoryAddrInfo count=%s, system id: %s', #self.cached_mem_info, self.system_id)
    end)
    return self.cached_mem_info
end

local function construct_result_obj(result_json)
    local obj = {}
    local msg_json = result_json['Messages']
    if not msg_json then
        return obj
    end
    for _, v in pairs(msg_json) do
        local related_prop = v['RelatedProperties']
        if related_prop then
            for _, prop_str in pairs(related_prop) do
                obj[string.match(prop_str, "/(.+)")] = 1
            end
        end
    end
    return obj
end

local function write_setting_log(result_json, setting_json, cur_json, system_id)
    if not result_json or not setting_json or not cur_json then
        log:error('[bios]write setting log fail:json is nil')
        return false
    end
    local result_obj = construct_result_obj(result_json)
    local context = initiator.new('IPMI', 'N/A', 'HOST')
    local bios_ser = bios_factory.get_service('bios_service')
    for prop, val in pairs(setting_json) do
        if not result_obj[prop] then
            local cur_val = cur_json[prop] or ''
            -- 21700BE0是带内上报配置信息的编码
            if bios_ser and bios_ser:is_multihost() then
                log:system(system_id):operation(context, 'BIOS', 'Set %s from [%s] to [%s] success,EvtCode:%s',
                prop, cur_val, val, '21700BE0')
            else
                log:operation(context, 'BIOS', 'Set %s from [%s] to [%s] success,EvtCode:%s',
                prop, cur_val, val, '21700BE0')
            end
        end
    end
    log:notice('[bios] operator result file done')
end

function bios_object:result_postprocess()
    local file_name = self.obj.ResultFileName
    local result_jso = bs_util.safe_get_file_json(file_name)

    if not result_jso or type(result_jso) ~= 'table' then
        log:error('bios_result_postprocess: result_jso is nil or result_jso is not json format!')
        return
    end

    pcall(function()
        write_setting_log(result_jso, bs_util.safe_get_file_json(self.obj.SettingFileName),
            bs_util.safe_get_file_json(self.obj.CurrentValueFileName), self.system_id)
    end)
    result_jso['Time'] = syslog_core.get_log_time_str(syslog_core.LOG_US_TIME)
    local json_data = json.encode(result_jso)
    bs_util.write_file_data(file_name, json_data)
end

function bios_object:bios_sync_demt_cfg(current_jso)
    pcall(function()
        return self:set_print_flag(current_jso['SYSDBGLevel'])
    end)
    local demt_jso = current_jso['PowerSaving']
    if not demt_jso then
        log:error('bios_sync_demt_cfg: get PowerSaving fail')
        return
    end
    local demt_cfg_new_value = prop_def.BIOS_DEMT_CFG_DISABLED
    if demt_jso == prop_def.BIOS_DEMT_ENABLED then
        demt_cfg_new_value = prop_def.BIOS_DEMT_CFG_ENABLED
    elseif demt_jso == prop_def.BIOS_DEMT_DISABLED then
        demt_cfg_new_value = prop_def.BIOS_DEMT_CFG_DISABLED
    end

    local demt_cfg_old_value = self:get_prop('DEMTConfig')
    if demt_cfg_old_value ~= demt_cfg_new_value then
        self:set_prop('DEMTConfig', demt_cfg_new_value)
    end

    log:info(
        'bios_sync_demt_cfg: bios sync demt cfg from currentvalue.json succeed,old value is %d, new value is %d',
        demt_cfg_old_value, demt_cfg_new_value)
end

function bios_object:bios_dynamic_config_sync()
    local current_jso = bs_util.get_file_json(self.obj.CurrentValueFileName)
    if not current_jso or current_jso == '' then
        log:error('bios_dynamic_config_sync: current_jso is nil!')
        return
    end

    self:bios_sync_demt_cfg(current_jso)
end

function bios_object:get_bios_obj()
    return self.obj
end

function bios_object:get_obj()
    return self.obj
end

function bios_object:update_bios_startup_state(state)
    self:set_prop('SystemStartupState', state:value())
end

function bios_object:get_bios_version()
    return self.obj.Version
end

function bios_object:get_bios_backup_version()
    return self.obj.BackupVersion
end

function bios_object:update_bios_backup_version(version)
    self.info_db.BackupVersion = version
    self.info_db:save()
    self.obj.BackupVersion = version
end

function bios_object:get_bios_id()
    return self.private_obj.bios_id
end

function bios_object:get_manu_id()
    local manu_id = self.private_obj.manu_id
    local bin_id = s_pack('I3', manu_id)
    local res = s_unpack('>I3', bin_id)
    return res
end

function bios_object:set_bios_version(version)
    self:set_prop('BiosBootStage', 255)
    local valid_version = get_valid_version(version)
    self.info_db.Version = valid_version
    self.info_db:save()
    self.obj.Version = valid_version
    skynet.fork(function ()
        self.firmware:update_bios_version(valid_version)
    end)
end

function bios_object:set_firmware_version(version)
    self.firmware:update_bios_version(version)
end

function bios_object:set_firmware_date(date)
    self.firmware:update_bios_firmware_date(date)
end

function bios_object:set_teeos_version(version)
    local valid_version = get_valid_version(version)
    self:set_prop('TeeOSVersion', valid_version)
    self.firmware:update_teeos_version(valid_version)
end

function bios_object:get_current_value_file_name()
    return self.obj.CurrentValueFileName
end

local print_flag_tbl<const> = {
    ["Enabled"] = prop_def.ENABLED,
    ["Disabled"] = prop_def.DISABLED
}

function bios_object:set_print_flag(flag)
    local print_flag = print_flag_tbl[flag]
    if print_flag == nil then
        print_flag = 0
    end
    if self.obj.BiosLogPrintEnabled then
        self.obj.BiosLogPrintEnabled = print_flag
    end
end

function bios_object:set_db_prop(prop, value)
    if self.info_db[prop] then
        self.info_db[prop] = value
        self.info_db:save()
    elseif self.config_db[prop] ~= nil then
        self.config_db[prop] = value
        self.config_db:save()
    end
end

function bios_object:set_private_prop(prop, value)
    if self.private_obj[prop] == nil then
        return false
    end
    self.private_obj[prop] = value
    self:set_db_prop(prop, value)
    return true
end

function bios_object:set_public_prop(prop, value)
    if self.obj[prop] == nil then
        return false
    end
    self.obj[prop] = value
    self:set_db_prop(prop, value)
    return true
end

function bios_object:set_prop(prop, value)
    if self:set_private_prop(prop, value) then
        return true
    end
    if self:set_public_prop(prop, value) then
        return true
    end
    return false
end

function bios_object:get_prop(prop)
    local private_prop = self.private_obj[prop]
    if private_prop then
        return private_prop
    end
    return self.obj[prop]
end

function bios_object:get_file_change()
    return self.private_obj.FileChangeFlag
end

function bios_object:set_file_change(changed, channel, num)
    self:set_prop('FileChangeFlag', changed)
    self:set_prop('FileChannel', channel)
    self:set_prop('FileNum', num)
end

function bios_object:set_ineffective()
    self:set_prop('SettingsEffectiveStatus', 'Ineffective')
    log:notice('[bios]set setting effective status to Ineffective')
end

function bios_object:set_effective()
    self:set_prop('SettingsEffectiveStatus', 'Effective')
    log:notice('[bios]set setting effective status to Effective')
end

-- ipmi:密码
local function response_ipmi_password(complete_code, res_data)
    local bin_data = ''
    if res_data ~= '' then
        local bin_manu_id = s_pack('I3', res_data.ManufactureId)
        bin_data = bin_manu_id .. res_data.Information
    end

    return msg.UpdateBiosPasswordRsp.new(complete_code, bin_data)
end

function bios_object:get_password_string(old_pwd, new_pwd)
    local text = {}
    text[1] = string.char(#old_pwd)
    text[2] = old_pwd
    text[3] = string.char(#new_pwd)
    text[4] = new_pwd
    return table.concat(text)
end

function bios_object:wait_pwd_ack_timeout(password_type)
    skynet.fork_once(function()
        -- 12秒超时
        skynet.sleep(12 * 100)
        self.task_status[password_type] = prop_def.BIOS_CHANGE_PWD_TASK_IDLE
        log:notice('[bios]wait password ack timeout: reset password(%u) status.', password_type)
    end)
end

function bios_object:get_password_resp(password_type, ctx)
    local old_pwd = ''
    local new_pwd = ''
    local kmc_ser = bios_factory.get_service('kmc_service')
    if not kmc_ser then
        log:error('[bios] save_encrypt_password: kmc service is null.')
        return response_ipmi_password(comp_code.UnspecifiedError, '')
    end

    if password_type == prop_def.BIOS_CHANGE_SUP_PWD_TYPE_SETUP then
        old_pwd = kmc_ser:decrypt_password(self:get_prop(obj_def.PROPERTY_BIOS_SETUP_PWD_OLD))
        new_pwd = kmc_ser:decrypt_password(self:get_prop(obj_def.PROPERTY_BIOS_SETUP_PWD_NEW))
    elseif password_type == prop_def.BIOS_CHANGE_USER_PWD_TYPE_SETUP then
        old_pwd = kmc_ser:decrypt_password(self:get_prop(
            obj_def.PROPERTY_BIOS_USER_SETUP_PWD_OLD))
        new_pwd = kmc_ser:decrypt_password(self:get_prop(
            obj_def.PROPERTY_BIOS_USER_SETUP_PWD_NEW))
    else
        log:error('[bios] password type[%u] is not supported.', password_type)
        return response_ipmi_password(comp_code.UnspecifiedError, '')
    end

    if not old_pwd or not new_pwd then
        log:error('[bios] decrypt password type[%u] fail.', password_type)
        return response_ipmi_password(comp_code.UnspecifiedError, '')
    end

    self.task_status[password_type] = prop_def.BIOS_CHANGE_PWD_TASK_WAIT_ACK
    self:wait_pwd_ack_timeout(password_type)
    local resp_data = {}
    resp_data.ManufactureId = self:get_manu_id()
    resp_data.Information = self:get_password_string(old_pwd, new_pwd)
    log:notice('[bios]get password(%u) finish.', password_type)
    self:clear_encrypt_password(password_type)
    local role = 'Supervisor'
    if password_type == prop_def.BIOS_CHANGE_USER_PWD_TYPE_SETUP then
        role = 'UserPassword'
    end
    ipmi.ipmi_operation_log(ctx, 'BIOS',
        'The request for getting BIOS %s password is delivered', role)
    return response_ipmi_password(comp_code.Success, resp_data)
end

function bios_object:get_setting_password(req, ctx)
    local task_status = self.task_status
    if not task_status[req.PasswordType] or task_status[req.PasswordType] ~=
        prop_def.BIOS_CHANGE_PWD_TASK_IDLE then
        log:error('[bios]get_setting_password: request is not supported in present state.')
        return response_ipmi_password(comp_code.CommandNotAvailable, '')
    end

    return self:get_password_resp(req.PasswordType, ctx)
end

function bios_object:clear_encrypt_password(password_type)
    if password_type == prop_def.BIOS_CHANGE_SUP_PWD_TYPE_SETUP then
        self:set_prop(obj_def.PROPERTY_BIOS_SETUP_PWD_OLD, '')
        self:set_prop(obj_def.PROPERTY_BIOS_SETUP_PWD_NEW, '')
    elseif password_type == prop_def.BIOS_CHANGE_USER_PWD_TYPE_SETUP then
        self:set_prop(obj_def.PROPERTY_BIOS_USER_SETUP_PWD_OLD, '')
        self:set_prop(obj_def.PROPERTY_BIOS_USER_SETUP_PWD_NEW, '')
    end
end

function bios_object:generate_pwd_set_event(assert, system_id)
    local param = {
        ComponentName = "System",
        State = (assert == 1 and 'true' or 'false'),
        EventKeyId = 'Bios.SystemSetConfigFailed',
        MessageArgs = json.encode({"password"}),
        SystemId = tostring(system_id),
        ManagerId = '1',
        ChassisId = '1',
        NodeId = ''
    }
    local ok, ret = pcall(event.generate_event, param)
    if not ok then
        log:error("add event failed. err:%s", ret)
    end
end

function bios_object:ack_setting_password(req, ctx)
    local task_status = self.task_status
    local password_type = req.PasswordType
    if not task_status[password_type] or task_status[password_type] ~=
        prop_def.BIOS_CHANGE_PWD_TASK_WAIT_ACK then
        log:error('[bios]ack_setting_password: request is not supported in present state.')
        return response_ipmi_password(comp_code.CommandNotAvailable, '')
    end

    if not req.Information or #req.Information ~= 1 then
        log:error('[bios]ack_setting_password: information invalid.')
        return response_ipmi_password(comp_code.CommandNotAvailable, '')
    end

    log:notice('[bios]ack setting password(%u) finish.', password_type)
    task_status[password_type] = prop_def.BIOS_CHANGE_PWD_TASK_IDLE
    self:clear_encrypt_password(password_type)
    local res_code = s_unpack('I1', req.Information)
    local role = 'Supervisor'
    if password_type == prop_def.BIOS_CHANGE_USER_PWD_TYPE_SETUP then
        role = 'UserPassword'
    end
    if res_code == prop_def.BIOS_CHANGE_PWD_OK then
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Change BIOS %s password successfully', role)
    else
        self:generate_pwd_set_event(0, self.system_id)
        self:generate_pwd_set_event(1, self.system_id)
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Change BIOS %s password failed', role)
    end
    return response_ipmi_password(comp_code.Success, '')
end

function bios_object:save_encrypt_password(type, old_pwd, new_pwd)
    local kmc_ser = bios_factory.get_service('kmc_service')
    if not kmc_ser then
        log:error('[bios] save_encrypt_password: kmc service is nil.')
        return prop_def.RESPONSE_ERROR
    end

    if type == prop_def.BIOS_PWD_NAME_SUPERVISOR or type == prop_def.BIOS_PWD_NAME_ADMIN then
        local res = self:set_prop(obj_def.PROPERTY_BIOS_SETUP_PWD_OLD,
            kmc_ser:encrypt_password(old_pwd))
        if not res then
            log:error('[bios] save_encrypt_password: save Supervisor old password failed.')
            return prop_def.RESPONSE_ERROR
        end
        res = self:set_prop(obj_def.PROPERTY_BIOS_SETUP_PWD_NEW,
            kmc_ser:encrypt_password(new_pwd))
        if not res then
            log:error('[bios] save_encrypt_password: save Supervisor new password failed.')
            return prop_def.RESPONSE_ERROR
        end
    elseif type == prop_def.BIOS_PWD_NAME_USER then
        local res = self:set_prop(obj_def.PROPERTY_BIOS_USER_SETUP_PWD_OLD,
            kmc_ser:encrypt_password(old_pwd))
        if not res then
            log:error('[bios] save_encrypt_password: save User old password failed.')
            return prop_def.RESPONSE_ERROR
        end
        res = self:set_prop(obj_def.PROPERTY_BIOS_USER_SETUP_PWD_NEW,
            kmc_ser:encrypt_password(new_pwd))
        if not res then
            log:error('[bios] save_encrypt_password: save User new password failed.')
            return prop_def.RESPONSE_ERROR
        end
    else
        log:error('[bios] save_encrypt_password: unsupported password.')
        return prop_def.METHOD_BIOS_CHANGE_PWD_UNSUPPORTED
    end

    return prop_def.RESPONSE_OK
end

-- 设置启动顺序
function bios_object:write_order_to_json(boot_priority_json)
    local path = self.obj.SettingFileName
    if not path or not vos.get_file_accessible(path) then
        log:debug('Set boot order:write_file setting path not exist')
        return false
    end

    local setting_json = bs_util.get_file_json(path)
    if not setting_json then
        setting_json = {}
    end

    for prop, val in pairs(boot_priority_json) do
        setting_json[prop] = val
    end

    local json_str = json.encode(setting_json)
    local err_code = bs_util.write_file_data(path, json_str)
    if err_code == prop_def.E_FAILURE then
        log:error('Set boot order failed: write_file_data fail')
        return false
    end

    return true
end

function bios_object:get_web_config(file_type)
    local cfg_tbl = json.json_object_new_array()
    self.config:traverse(cfg_tbl)
    local res = json.json_object_ordered_encode(cfg_tbl)
    skynet.fork(function()
        collectgarbage('collect')
    end)
    return res
end

function bios_object:get_config_data(file_type)
    if file_type == 'WebConfig' then
        return self:get_web_config()
    end
    return self.cpt_manager:get_data(file_type)
end

local component_files<const> = {
    ComponentVersion = 'ComponentVersion.json',
    PolicyConfigRegistry = 'PolicyConfigRegistry.json',
    ConfigValue = 'ConfigValue.json'
}

function bios_object:collect_json_file(path)
    local dst_path
    for prop, file in pairs(JSON_FILES) do
        dst_path = path .. '/' .. file
        file_util.copy_file(self.obj[prop], dst_path)
        utils_core.chmod_s(dst_path, utils.S_IRUSR | utils.S_IRGRP)
        skynet.sleep(10)
    end

    for _, file in pairs(component_files) do
        dst_path = path .. '/' .. file
        file_util.copy_file(bs_util.get_conf_path(self.system_id, file), dst_path)
        utils_core.chmod_s(dst_path, utils.S_IRUSR | utils.S_IRGRP)
        skynet.sleep(10)
    end

    for _, file in pairs(SILK_FILES) do
        dst_path = path .. '/' .. file
        file_util.copy_file(bs_util.get_conf_path(self.system_id, file), dst_path)
        utils_core.chmod_s(dst_path, utils.S_IRUSR | utils.S_IRGRP)
        skynet.sleep(10)
    end
end

function bios_object:get_dump_info()
    local props = self.obj:get_all_prop_names()
    local log_info = {}
    table.insert(log_info, '------Bios tree Begin------\r\n')
    local info
    for _, prop in pairs(props) do
        info = string.format('%s:%s', prop, bs_util.dump_prop(prop, self.obj[prop]))
        table.insert(log_info, info)
        table.insert(log_info, '\r\n')
        skynet.sleep(10)
    end
    return table.concat(log_info)
end

return bios_object